<?php
namespace App\Http\Limter;


use App\Exception\LimitedException;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Bean\BeanFactory;
use Swoft\Stdlib\Helper\Arr;

/**
 * Class RateLimiter
 * @package App\Http\Limter
 * @Bean("Limiter")
 */
class RateLimiter
{

    /**
     * @var string
     */
    private $name = 'swoft:limiter';

    /**
     * Unit is `qps/seconds`
     *
     * @var int
     */
    private $rate = 10;

    /**
     * Max tokens
     *
     * @var int
     */
    private $max = 20;

    /**
     * @var int
     */
    private $default = 10;

    /**
     * @param string $className
     * @param string $method
     * @param $config
     * @param callable $func
     * @return mixed
     * @throws LimitedException
     */
    public function checkRate( string $className, string $method, $config, callable $func)
    {
        $commonConfig = [
            'name'    => $this->name,
            'rate'    => $this->rate,
            'max'     => $this->max,
            'default' => $this->default
        ];

        // Default Key
        $config['key'] = $config['key'] ?? md5(sprintf('%s:%s', $className, $method));

        $config   = Arr::merge($commonConfig, $config);
        $fallback = $config['fallback'] ?? '';

        $rateLimiter = BeanFactory::getBean("redisRateLimiter");
        $ticket = $rateLimiter->getTicket($config);

        if ($ticket) {  // 有令牌  继续执行
            return $func();
        }

        if (!empty($fallback)) { // 没有令牌 降级
            return $fallback();
        }

        throw new LimitedException();
    }

}
